#ifndef ADOBE_UNICODE_HPP
#define ADOBE_UNICODE_HPP

/*************************************************************************************************/

#include <cassert>
#include <stdexcept>

#include "string.hpp"

/*************************************************************************************************/

namespace adobe {


template <bool B, class T = void>
struct enable_if_c { typedef T type; };
template <class T>
struct enable_if_c<false, T> {};

template <class Cond, class T = void> 
struct enable_if : public enable_if_c<Cond::value, T> {};

//using sqlitepp::utf8_char;
typedef unsigned char utf8_char;
using sqlitepp::utf16_char;
using sqlitepp::utf32_char;


template <typename T>
struct is_utf8_type
{ enum { value = sizeof(T) == 1 }; };

/*************************************************************************************************/

template <typename T>
struct is_utf16_type
{ enum { value = sizeof(T) == 2 }; };

/*************************************************************************************************/

template <typename T>
struct is_utf32_type
{ enum { value = sizeof(T) == 4 }; };

/*************************************************************************************************/

template <typename I>
struct is_utf8_iterator_type
{ enum { value = is_utf8_type<typename std::iterator_traits<I>::value_type>::value }; };

/*************************************************************************************************/

template <typename I>
struct is_utf16_iterator_type
{ enum { value = is_utf16_type<typename std::iterator_traits<I>::value_type>::value }; };

/*************************************************************************************************/

template <typename I>
struct is_utf32_iterator_type
{ enum { value = is_utf32_type<typename std::iterator_traits<I>::value_type>::value }; };

/*************************************************************************************************/

namespace implementation {

/*************************************************************************************************/

const utf8_char	    to_utf32_pivot_1_k(128);
const utf8_char     to_utf32_pivot_2_k(192);
const utf8_char     to_utf32_pivot_3_k(224);
const utf8_char     to_utf32_pivot_4_k(240);
const utf8_char     to_utf32_pivot_5_k(248);
const utf8_char     to_utf32_pivot_6_k(252);
const utf8_char     to_utf32_pivot_7_k(254);

const utf32_char	to_utf8_pivot_1_k(1UL << 7);
const utf32_char	to_utf8_pivot_2_k(1UL << 11);
const utf32_char	to_utf8_pivot_3_k(1UL << 16);
const utf32_char	to_utf8_pivot_4_k(1UL << 21);
const utf32_char	to_utf8_pivot_5_k(1UL << 26);

const utf16_char	to_utf16_surrogate_pivot_k(65535);
const utf16_char	utf16_high_surrogate_front_k(0xd800);
const utf16_char	utf16_high_surrogate_back_k(0xdbff);
const utf16_char	utf16_low_surrogate_front_k(0xdc00);
const utf16_char	utf16_low_surrogate_back_k(0xdfff);

/*************************************************************************************************/

template <std::size_t NumBytes> struct utf8_header_t	{ };
template <>						struct utf8_header_t<0>	{ enum { value = char(0x80) }; }; // nonheader
//template <>					struct utf8_header_t<1>	{ enum { value = char(0x00) }; }; // illegal
template <>						struct utf8_header_t<2>	{ enum { value = char(0xC0) }; };
template <>						struct utf8_header_t<3>	{ enum { value = char(0xE0) }; };
template <>						struct utf8_header_t<4>	{ enum { value = char(0xF0) }; };
template <>						struct utf8_header_t<5>	{ enum { value = char(0xF8) }; };
template <>						struct utf8_header_t<6>	{ enum { value = char(0xFC) }; };

/*************************************************************************************************/

template <char Mask, typename BinaryInteger>
inline char add_mask(BinaryInteger code)
{ return char(code | Mask); }

template <std::size_t NumBytes, bool Header, typename BinaryInteger>
inline char utf8_add_mask(BinaryInteger code)
{ return add_mask<utf8_header_t<Header ? NumBytes : 0>::value>(code); }

/*************************************************************************************************/

template<char Mask, typename BinaryInteger>
inline char strip_mask(BinaryInteger code)
{ return char(code & ~Mask); }

template <std::size_t NumBytes, bool Header, typename BinaryInteger>
inline char utf8_strip_mask(BinaryInteger code)
{ return strip_mask<utf8_header_t<Header ? NumBytes : 0>::value>(code); }

/*************************************************************************************************/

template <std::size_t Position>
inline utf32_char promote_fragment(char fragment)
{ return utf32_char(fragment << ((Position - 1) * 6)); }

template <>
inline utf32_char promote_fragment<1>(char fragment)
{ return utf32_char(fragment); }

template <>
inline utf32_char promote_fragment<0>(char); // unimplemented

/*************************************************************************************************/

template <std::size_t Position>
inline char demote_fragment(utf32_char fragment)
{ return char((fragment >> ((Position - 1) * 6)) & 0x0000003F); }

template <>
inline char demote_fragment<1>(utf32_char fragment)
{ return char(fragment & 0x0000003F); }

template <>
inline char demote_fragment<0>(utf32_char); // unimplemented

/*************************************************************************************************/

template <std::size_t ByteCount, bool Header = true>
struct demotion_engine_t
{
	template <typename OutputIterator>
	inline OutputIterator operator () (utf32_char code, OutputIterator i)
	{
		*i = utf8_add_mask<ByteCount, Header>(demote_fragment<ByteCount>(code));
		return demotion_engine_t<ByteCount - 1, false>()(code, ++i);
	}
};

template <>
struct demotion_engine_t<1, false>
{
	template <typename OutputIterator>
	inline OutputIterator operator () (utf32_char code, OutputIterator i)
	{
		*i = utf8_add_mask<0, false>(demote_fragment<1>(code));
		return ++i;
	}
};

/*************************************************************************************************/

template <std::size_t ByteCount, bool Header = true>
struct promotion_engine_t
{
	template <typename InputIterator>
	inline utf32_char operator () (InputIterator& first, InputIterator last)
	{
		/*
			CodeWarrior 9.4 doesn't like this code composited into one line;
			GCC doesn't seem to have a problem.
		*/

		char			n(*first);
		char			stripped(utf8_strip_mask<ByteCount, Header>(n));
		utf32_char	shifted(promote_fragment<ByteCount>(stripped));

		++first;

		if (first == last) throw std::runtime_error("unicode: utf32 conversion ran out of input");

		return shifted | promotion_engine_t<ByteCount - 1, false>()(first, last);
	}
};

template <>
struct promotion_engine_t<1, false>
{
	template <typename InputIterator>
	inline utf32_char operator () (InputIterator& first, InputIterator)
	{
		utf32_char result(promote_fragment<1>(utf8_strip_mask<0, false>(*first)));

		++first;

		return result;
	}
};

/*************************************************************************************************/

template <typename InputIterator, typename DestInteger>
inline typename enable_if<is_utf16_iterator_type<InputIterator>, InputIterator>::type
	to_utf32 (InputIterator first, InputIterator last, DestInteger& result)
{
	utf16_char code(static_cast<utf16_char>(*first));

	++first;

	if (code >= implementation::utf16_high_surrogate_front_k &&
		code <= implementation::utf16_high_surrogate_back_k)
	{
		result = 0;

		if (first == last)
			throw std::runtime_error("unicode: utf16 high surrogate found without low surrogate"); 

		utf16_char low(static_cast<utf16_char>(*first));

		assert (low >= implementation::utf16_low_surrogate_front_k &&
				low <= implementation::utf16_low_surrogate_back_k);

		++first;

		result =	(code - implementation::utf16_high_surrogate_front_k) * 0x400 +
					(low - implementation::utf16_low_surrogate_front_k) + 0x10000;
	}
	else if (	code >= implementation::utf16_low_surrogate_front_k &&
				code <= implementation::utf16_low_surrogate_back_k)
		{ throw std::runtime_error("unicode: utf16 low surrogate found without high surrogate"); }
	else
		{ result = static_cast<DestInteger>(code); }

	return first;
}

/*************************************************************************************************/

template <typename InputIterator, typename DestInteger>
inline typename enable_if<is_utf8_iterator_type<InputIterator>, InputIterator>::type
	to_utf32 (InputIterator first, InputIterator last, DestInteger& result)
{
	unsigned char n(static_cast<unsigned char>(*first));

	if (n < implementation::to_utf32_pivot_1_k)
		{ result = static_cast<DestInteger>(n); ++first; }
	else if (n < implementation::to_utf32_pivot_2_k)
		{ throw std::runtime_error("unicode: ill-defined utf8 (< 192)"); }
	else if (n < implementation::to_utf32_pivot_3_k)
		result = implementation::promotion_engine_t<2>()(first, last);
	else if (n < implementation::to_utf32_pivot_4_k)
		result = implementation::promotion_engine_t<3>()(first, last);
	else if (n < implementation::to_utf32_pivot_5_k)
		result = implementation::promotion_engine_t<4>()(first, last);
	else if (n < implementation::to_utf32_pivot_6_k)
		result = implementation::promotion_engine_t<5>()(first, last);
	else if (n < implementation::to_utf32_pivot_7_k)
		result = implementation::promotion_engine_t<6>()(first, last);
	else
		{ throw std::runtime_error("unicode: ill-defined utf8 (>= 254)"); }

	return first;
}

/*************************************************************************************************/

} // namespace implementation

/*************************************************************************************************/


/*************************************************************************************************/
/*
		utf32 -> utf8
			- 1 source value
			- n output values
*/

template <	typename I, // I models Integer; I must be a valid UTF32-encoded code point
			typename O>	// O models OutputIterator
inline typename enable_if<is_utf32_type<I>, O>::type
	to_utf8(I code, O output)
{
	if (code < implementation::to_utf8_pivot_1_k) // UTF-8 is 1 byte long
		{ *output = static_cast<char>(code); ++output; }
	else if (code < implementation::to_utf8_pivot_2_k) // UTF-8 is 2 bytes long
		output = implementation::demotion_engine_t<2>()(code, output);
	else if (code < implementation::to_utf8_pivot_3_k) // UTF-8 is 3 bytes long
		output = implementation::demotion_engine_t<3>()(code, output);
	else if (code < implementation::to_utf8_pivot_4_k) // UTF-8 is 4 bytes long
		output = implementation::demotion_engine_t<4>()(code, output);
	else if (code < implementation::to_utf8_pivot_5_k) // UTF-8 is 5 bytes long
		output = implementation::demotion_engine_t<5>()(code, output);
	else // UTF-8 is 6 bytes long
		output = implementation::demotion_engine_t<6>()(code, output);

	return output;
}

/*************************************************************************************************/
/*
		utf16 -> utf8
			- 1 source value
			- n output values
*/

template <	typename I, // I models Integer; I must be a valid UTF32-encoded code point
			typename O>	// O models OutputIterator
inline typename enable_if<is_utf16_type<I>, O>::type
	to_utf8(I code, O output)
{
	return to_utf8(static_cast<utf32_char>(code), output);
}

/*************************************************************************************************/
/*
		utf16 -> utf8
			- n source values
			- m output values
*/

template <	typename I, // I models InputIterator
			typename O> // O models OutputIterator
inline typename enable_if<is_utf16_iterator_type<I>, O>::type
	to_utf8(I first, I last, O output)
{
	while (first != last)
	{
		utf32_char	result;

		first = implementation::to_utf32(first, last, result);

		output = to_utf8(result, output);
	}
	return output;
}

/*************************************************************************************************/
/*
		utf32 -> utf8
			- n source values
			- m output values
*/

template <	typename I, // I models InputIterator
			typename O> // O models OutputIterator
inline typename enable_if<is_utf32_iterator_type<I>, O>::type
	to_utf8(I first, I last, O output)
{
    while ( first != last )
    {
        output = to_utf8(*first, output);
        ++first;
    }
	return output;
}

/*************************************************************************************************/
/*
		utf32 -> utf16
			- 1 source value
			- n output values
*/

template <	typename I, // I models Integer; sizeof(I) must equal 4; code must be valid utf32
			typename O>	// O models OutputIterator
inline typename enable_if<is_utf32_type<I>, O>::type
	to_utf16(I code, O output)
{
	if (code <= implementation::to_utf16_surrogate_pivot_k)
	{
		*output = static_cast<utf16_char>(code);
	}
	else
	{
		*output = static_cast<utf16_char>((code - 0x10000) / 0x400 + implementation::utf16_high_surrogate_front_k);

		++output;

		*output = static_cast<utf16_char>((code - 0x10000) % 0x400 + implementation::utf16_low_surrogate_front_k);
	}
	return ++output;
}

/*************************************************************************************************/
/*
		utf8 -> utf16
			- n source values
			- m output values
*/
template <	typename I, // I models InputIterator
			typename O> // O models OutputIterator
inline typename enable_if<is_utf8_iterator_type<I>, O>::type
	to_utf16(I first, I last, O output)
{
	while (first != last)
	{
		utf32_char	result;

		first = implementation::to_utf32(first, last, result);

		output = to_utf16(result, output);
	}
	return output;
}

/*************************************************************************************************/
/*
		utf32 -> utf16
			- n source values
			- m output values
*/
template <	typename I, // I models InputIterator
			typename O> // O models OutputIterator
inline typename enable_if<is_utf32_iterator_type<I>, O>::type
	to_utf16(I first, I last, O output)
{
	while (first != last)
	{
		output = to_utf16(*first, output);
		++first;
	}
	return output;
}

/*************************************************************************************************/
/*
	Precondition: [ first, last ) must convert to exactly one UTF-16 character
*/

template <typename I>
inline typename enable_if<is_utf8_iterator_type<I>, utf16_char>::type
	to_utf16(I first, I last)
{
	utf32_char result;

	implementation::to_utf32(first, last, result);
	return static_cast<utf16_char>(result);
}

/*************************************************************************************************/
/*
		utf16 -> utf32
			- n source values
			- m output values

		utf8 -> utf32
			- n source values
			- m output values
*/

template < typename I, // I models InputIterator
			typename O>	// O models OutputIterator
inline O to_utf32(I first, I last, O output)
{
	utf32_char result;

	while (first != last)
	{
		first = implementation::to_utf32(first, last, result);

		*output = result;

		++output;
	}
	return output;
}

/*************************************************************************************************/
/*
	Precondition: [ first, last ) must convert to exactly one UTF-32 character
*/

template <typename I> // I models InputIterator
inline utf32_char to_utf32(I first, I last)
{
	utf32_char result;

	implementation::to_utf32(first, last, result);
	return result;
}

/*************************************************************************************************/

} // namespace

/*************************************************************************************************/

#endif
	
/*************************************************************************************************/
